#include "timer.h"
#include "queueapi.h"

enum
{
    NUM_TICK_WHEEL = CFG_WHEEL_MASK + 1
};

static struct Queue tick_wheel[NUM_TICK_WHEEL];
static int wheel_ptr;
static u64 mirror_tick;

typedef struct WheelTimer
{
    AstralTickTimer timer;
    struct QueueNode node;
    u64 target;
} *WheelTimer;

void ttimer_init()
{
    for(int i=0; i<NUM_TICK_WHEEL; i++)
    {
        queue_init(&tick_wheel[i]);
    }
}


static WheelTimer new_ttimer_wrapper(int id)
{
#ifndef CFG_NUM_TTIMER
#define CFG_NUM_TTIMER (100)
#endif
    static struct WheelTimer timer_node[CFG_NUM_TTIMER];
    if(id<CFG_NUM_TTIMER)
    {
        return &timer_node[id];
    }
    return CAST_AS(WheelTimer, NULL);
}


void active_ttimer(AstralTickTimer timer)
{
    WheelTimer wt = new_ttimer_wrapper(timer->id);
    wt->timer = timer;
    u64 target = (mirror_tick + timer->timeout);
    int slot = target & CFG_WHEEL_MASK; //only 32 slot valid
    wt->target = target;
    queue_push(&tick_wheel[slot], &wt->node);
}

void deact_ttimer(AstralTickTimer timer)
{
    WheelTimer wt = new_ttimer_wrapper(timer->id);
    Queue q = wt->node.queue;
    if(wt->timer->is_oneshot)
    {
        queue_remove(q, &wt->node);
    }
    else
    {
        wt->timer->is_oneshot = True;
    }
}

void ack_tick_irq()
{
    mirror_tick++;
    wheel_ptr = mirror_tick & CFG_WHEEL_MASK;
    Queue q = &tick_wheel[wheel_ptr];

    QueueNode n = q->first;
    while (n)
    {
        QueueNode next = n->next;
        WheelTimer wt = CONTAINER_OF(WheelTimer, node, n);
        if (wt->target > mirror_tick)
        {
            n = next;
            continue;
        }
    
        // Astral TTimer only effects here
        executable callback = wt->timer->callback;
        object arg = wt->timer->arg;
        callback(arg);

        queue_remove(n->queue, n);
        if (!wt->timer->is_oneshot)
        {
            // period timer, update the next position
            u64 target = (mirror_tick + wt->timer->timeout);
            int slot = target & CFG_WHEEL_MASK;
            wt->target = target;
            queue_push(&tick_wheel[slot], n);
        }

        n = next;
    }
}

u64 get_tick_count()
{
    return mirror_tick;
}